// ignore_for_file: constant_identifier_names, unused_local_variable

import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'dart:math';

import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
import 'package:largefriends/config/lf_config.dart';
import 'package:largefriends/config/lf_im_type.dart';
import 'package:largefriends/entity/entities/pair.dart';
import 'package:largefriends/events/payment_success_event.dart';
import 'package:largefriends/res/lf_string_ext.dart';
import 'package:largefriends/service/im/lf_im_server_msg_ext.dart';
import 'package:largefriends/utils/lf_logger.dart';
import 'package:uuid/uuid.dart';
import 'package:web_socket_channel/io.dart';
import 'package:web_socket_channel/status.dart' as status;
import 'package:web_socket_channel/web_socket_channel.dart';

import '../../entity/entities/chat_new_message_entity.dart';
import '../../entity/entities/lf_message_entity.dart';
import '../manager/lf_event_helper.dart';
import '../../utils/lf_dialog_utils.dart';
import '../lf_auth_service.dart';

typedef MessageCallback = Function(LfMessageEntity messageEntity);

class LfIMService extends GetxService {
  bool isBackground = false;

  ///记录当前ping收到的时间
  int pingInterval = DateTime.now().millisecondsSinceEpoch;

  LfAuthService authService = Get.find();

  ///重连策略就是间隔1s，2s，3s，5s，8s... 类似一个斐波那契数列，如果超过一个小时，就按一个小时算
  final int maxInterval = 3600000;
  int lastTimeInterval = 0;
  int currentTimeInterval = 1000;

  String groupId = '127e3c4d959f150858cfc19e7caed460';

  ///生产站的groupId

  ///初始状态： 连接中。。。。
  var connectionStatus = (ConnectionStatusEnum.closed).obs;

  ///初始化网络监听不需要重连
  bool needReConnect = false;

  IOWebSocketChannel? channel;
  final List<MessageCallback> _messageCallBack = List.empty(growable: true);

  ///网络监听变化
  final Connectivity _connectivity = Connectivity();
  late StreamSubscription _connectivitySubscription,
      streamSubscriptionPaymentSuccess;
  final _connectionStatus = (ConnectivityResult.none).obs;

  get netWorkStatus => _connectionStatus.value;

  set netWorkStatus(val) => _connectionStatus.value = val;

  _cancelReconnect() {
    lastTimeInterval = 0;
    currentTimeInterval = 1000;
  }

  @override
  void onInit() {
    super.onInit();
    _initConnectivity();

    ///网络变化监听
    _connectivitySubscription =
        _connectivity.onConnectivityChanged.listen(_updateConnectionStatus);
    streamSubscriptionPaymentSuccess =
        LfEventHelper().getEvent().on<PaymentSuccessEvent>().listen((event) {
      ///如果升级成功了,需要把canchat map清空一下,防止无法聊天
      canChatMap.clear();
    });
  }

  addMessageCallBack(MessageCallback callback) {
    _messageCallBack.add(callback);
  }

  removeMessageCallBack(MessageCallback callback) {
    _messageCallBack.remove(callback);
  }

  Future connect() async {
    lfLogger.w("IMService connectionStatus:${connectionStatus.value}");
    if ((connectionStatus.value == ConnectionStatusEnum.connected &&
        channel != null &&
        channel!.closeCode == null)) {
      return;
    }
    connectionStatus.value = ConnectionStatusEnum.connecting;
    if (authService.loggedIn()) {
      int platformType = Platform.isIOS ? 1 : 2;
      String url =
          '$chatUrl${authService.tokenEntity!.accessToken}&platform=$platformType';
      lfLogger.w("IMService connect url: ${url.toString()}");
      try {
        channel = null;

        ///
        /// If there's an error connecting, the channel's stream emits a
        /// [WebSocketChannelException] wrapping that error and then closes.
        channel = IOWebSocketChannel.connect(
          url,
          // pingInterval: const Duration(seconds: 5)
        );
        channel?.stream.listen(_data, onError: _onError, onDone: _onDone);
      } on UnsupportedError catch (e) {
        connectionStatus.value = ConnectionStatusEnum.closed;
        lfLogger.w("IMService connect UnsupportedError error: ${e.toString()}");
      } on SocketException catch (e) {
        connectionStatus.value = ConnectionStatusEnum.closed;
        lfLogger.w("IMService connect SocketException error: ${e.toString()}");
      }
    } else {
      connectionStatus.value = ConnectionStatusEnum.closed;
    }
  }

  Future<void> _initConnectivity() async {
    late ConnectivityResult result;
    try {
      result = await _connectivity.checkConnectivity();
    } on PlatformException catch (e) {
      lfLogger.w('IMService---Couldn\'t check connectivity status $e');
      return;
    }
    return _updateConnectionStatus(result);
  }

  Future<void> _updateConnectionStatus(ConnectivityResult result) async {
    if (authService.loggedIn()) {
      if (result == ConnectivityResult.mobile ||
          result == ConnectivityResult.wifi) {
        if (connectionStatus.value == ConnectionStatusEnum.closed) {
          if (lastTimeInterval > 0) {
            //清除以前重连的时间间隔，马上进行重连
            _cancelReconnect();
          }
          if (needReConnect) {
            _tryReconnect();
          }
        }
      } else {
        connectionStatus.value = ConnectionStatusEnum.closed;
      }
    }
    _connectionStatus.value = result;
    lfLogger.w("IMService-----_connectionStatus: + $result");
  }

  disconnect() async {
    _cancelReconnect();
    _messageCallBack.clear();
    try {
      await channel?.sink.close(status.goingAway);
    } on WebSocketChannelException catch (e) {
      lfLogger.w('IMService-----$e');
    }
    channel = null;
    connectionStatus.value = ConnectionStatusEnum.closed;
    canChatMap.clear();
    messageMap.clear();
    lfLogger.w('IMService socket 通道关闭');
  }

  ///检测是断开状态就触发重连机制
  _tryReconnect() {
    //  /// Possible states of the connection.
    //   static const int connecting = 0;
    //   static const int open = 1;
    //   static const int closing = 2;
    //   static const int closed = 3;
    lfLogger.w(
        "IMService-----channel?.innerWebSocket?.readyState : ${channel?.innerWebSocket?.readyState}"
        "\n pingInterval:${DateTime.now().millisecondsSinceEpoch - pingInterval > 10 * 1000}");

    ///ping 的默认周期是30秒，如果60秒内都没收到，说明已经断开im链接了

    if (channel?.innerWebSocket?.readyState != WebSocket.open ||
        DateTime.now().millisecondsSinceEpoch - pingInterval >= 60 * 1000) {
      connectionStatus.value = ConnectionStatusEnum.closed;
    }

    if (connectionStatus.value == ConnectionStatusEnum.connected &&
        (channel != null && channel!.closeCode == null)) {
      return;
    }
    lfLogger.w("IMService-----tryReconnect");
    connectionStatus.value = ConnectionStatusEnum.connecting;
    int retryInterval = lastTimeInterval + currentTimeInterval;
    lastTimeInterval = currentTimeInterval;
    currentTimeInterval = retryInterval;

    lfLogger.w("IMService-----tryReconnect delay: + $retryInterval");

    Future.delayed(Duration(milliseconds: min(retryInterval, maxInterval)),
        () async {
      if (connectionStatus.value != ConnectionStatusEnum.connected) {
        await connect();
      }
    });
  }

  ///事件监听
  _data(event) {
    // lfLogger.w("IMService,_Data${event.toString()}");
    Map<String, dynamic> resultData = json.decode(event.toString());
    LfMessageEntity messageEntity = LfMessageEntity.fromJson(resultData);

    var type = MessageTypeEnum.values.firstWhere(
        (element) => element.value == messageEntity.type,
        orElse: () => MessageTypeEnum.unKnow);
    switch (type) {
      case MessageTypeEnum.ping:
        pingInterval = DateTime.now().millisecondsSinceEpoch;
        channel?.sink.add("{\"type\":\"pong\"}");
        break;
      case MessageTypeEnum.pong:
        channel?.sink.add("{\"type\":\"ping\"}");
        break;
      case MessageTypeEnum.connected:
        connectionStatus.value = ConnectionStatusEnum.connected;
        needReConnect = true;
        break;
      case MessageTypeEnum.recall:
        _recallMessage(messageEntity);
        break;
      case MessageTypeEnum.report:

        ///接收对方已读的消息时间
        break;
      case MessageTypeEnum.error:
        //{"type":"error","time":1606703469,"code":30001029,"message":"The user is unavailable now."}
        //{"type":"error","time":1606703469,"code":30003002,"message":"Input parameters error."}
        //{"type":"error","time":1606703469,"code":10001001,"message":"Please login first"}
        if (resultData.containsKey("message")) {
          showSnackBar(resultData['message']);
        }
        // if (resultData.containsKey("error_code") && resultData["error_code"] == 10001001) {
        //   if(!authService.tokenExpiredDialogCompleter.isCompleted) {
        //     debugPrint("tokenExpiredDialogCompleter: ${DateTime.now().microsecondsSinceEpoch}");
        //     authService.tokenExpiredDialogCompleter.complete();
        //     showCustomDialog(
        //         title: 'SESSION EXPIRED!',
        //         barrierDismissible: false,
        //         content: 'To protect your account, we\'ll log you out when you\'re away for a while. Please log in again.',
        //         leftText: 'LOG IN',
        //         leftClick: () {
        //           authService.tokenExpiredDialogCompleter = Completer();
        //           authService.loginOut();
        //         });
        //   }
        //
        // }
        break;
      case MessageTypeEnum.checkOnline:
        break;
      case MessageTypeEnum.say:
        receiveAndUpdateMessage(messageEntity);
        break;
      case MessageTypeEnum.groupConnected:
        if (messageEntity.groupType == "common") {
          groupId = messageEntity.groupId ?? '';
        }
        break;
      case MessageTypeEnum.sayToGroup:
        if (messageEntity.groupType == "common") {
          // receiveAndUpdateGroupMessage(messageEntity);
        }
        break;
      default:
        break;
    }
    for (var listener in _messageCallBack) {
      listener.call(messageEntity);
    }
  }

  _recallMessage(LfMessageEntity messageEntity) {
    if (messageEntity.groupId.isNullOrEmpty == false) {
      // receiveAndUpdateGroupMessage(messageEntity);
    } else {
      receiveAndUpdateMessage(messageEntity);
    }
  }

  _onError(error) {
    lfLogger.w("IMService error:$error");
    if (error is WebSocketChannelException) {
      connectionStatus.value = ConnectionStatusEnum.closed;
      _tryReconnect();
    }
  }

  _onDone() {
    lfLogger.w("IMService _onDone -----------------");
    if (!authService.loggedIn()) {
      return;
    }
    // tryReconnect();
  }

  bool isSameMessageEntity(
      ChatNewMessageEntity entity, LfMessageEntity responseEntity) {
    if ((entity.historyId.isNullOrEmpty == false) &&
        (responseEntity.messageId.isNullOrEmpty == false)) {
      return entity.historyId == responseEntity.messageId;
    }
    if ((entity.localId.isNullOrEmpty == false) &&
        (responseEntity.localId.isNullOrEmpty == false)) {
      return entity.localId == responseEntity.localId;
    }

    return false;
  }

  @override
  void onClose() {
    _messageCallBack.clear();
    _connectivitySubscription.cancel();
    streamSubscriptionPaymentSuccess.cancel();
    disconnect();
    super.onClose();
  }

  ///维护一个map列表,保存canChat信息
  final Map<String, String> canChatMap = {};

  final List<ChatNewMessageEntity> chatRoomMessages =
      List.empty(growable: true);

  /// 维护一个map,存储所有的消息列表
  final Map<String, List<ChatNewMessageEntity>> messageMap = {};
}

const double maxMessagePix = 200;

Pair pairWidthAndHeight(double width, double height) {
  var resizeWidth = maxMessagePix;
  var resizeHeight = maxMessagePix;
  if (width <= 300 || height <= 300) {
    return Pair(resizeWidth, resizeHeight);
  } else {
    if ((width >= height)) {
      if (width > maxMessagePix) {
        resizeWidth = maxMessagePix;
      } else if (width < maxMessagePix / 2) {
        resizeWidth = maxMessagePix / 2;
      } else {
        resizeWidth = width.toDouble();
      }
      resizeHeight = resizeWidth * height / width;
    } else {
      if (height > maxMessagePix) {
        resizeHeight = maxMessagePix;
      } else if (width < maxMessagePix / 2) {
        resizeHeight = maxMessagePix / 2;
      } else {
        resizeHeight = height.toDouble();
      }
      resizeWidth = resizeHeight * width / height;
    }
  }

  return Pair(resizeWidth, resizeHeight);
}

String createUUID() {
  if (Platform.isIOS) {
    return 'ios_${const Uuid().v4().toString()}';
  }
  return 'android_${const Uuid().v4().toString()}';
}
